﻿#include <occmesh/frame/OCCWidget.h>

#include "ui_MainWindow.h"
#include "ui_OCCWidget.h"

OCCWidget::OCCWidget(QWidget *parent)
    : QWidget(parent), ui(new Ui::OCCWidget), m_mode(Action3d::Nothing), m_shadeMode(ShadeMode::Smooth), m_pos({0, 0}),
      m_parent(static_cast<MainWindow *>(parent->parentWidget()))
{
    // 注意 m_parent 应该是父窗口的父窗口，因为上级窗口为 centralWidget
    ui->setupUi(this);

    // 逐层级启用鼠标追踪
    this->setMouseTracking(true);
    ui->frame->setMouseTracking(true);
    this->setUpdatesEnabled(false);
    ui->frame->setUpdatesEnabled(false);
}

OCCWidget::~OCCWidget()
{
    delete ui;
}

void OCCWidget::InitializeWindow()
{
    // 创建 OpenGL 引擎
    Handle(Aspect_DisplayConnection) theDisp = new Aspect_DisplayConnection();
    Handle(OpenGl_GraphicDriver) H_OpenGL_GraphicDriver = new OpenGl_GraphicDriver(theDisp);

    // 创建视口
    m_hViewer = new V3d_Viewer(H_OpenGL_GraphicDriver);

    // 光照
    Handle(V3d_DirectionalLight) LightDir = new V3d_DirectionalLight(V3d_Zneg, Quantity_Color(Quantity_NOC_GRAY97), 1);
    Handle(V3d_AmbientLight) LightAmb = new V3d_AmbientLight();

    // 设置光照方向
    LightDir->SetDirection(1.0, -2.0, -10.0);

    // 添加光照
    m_hViewer->AddLight(LightDir);
    m_hViewer->AddLight(LightAmb);
    m_hViewer->SetLightOn(LightDir);
    m_hViewer->SetLightOn(LightAmb);

    // 创建交互上下文
    m_hAisContext = new AIS_InteractiveContext(m_hViewer);
    m_structure = new Graphic3d_Structure(m_hViewer->StructureManager());
    m_structure->SetVisual(Graphic3d_TOS_SHADING);

    // 配置全局设置，渲染边界
    const Handle(Prs3d_Drawer) &contextDrawer = m_hAisContext->DefaultDrawer();
    if (!contextDrawer.IsNull())
    {
        const Handle(Prs3d_ShadingAspect) &SA = contextDrawer->ShadingAspect();
        const Handle(Graphic3d_AspectFillArea3d) &FA = SA->Aspect();
        contextDrawer->SetFaceBoundaryDraw(true);
        // contextDrawer->SetDeviationCoefficient(1e-5);  // 更高的精度

        FA->SetEdgeOff();

        // 设置最大绘制线路
        contextDrawer->SetMaximalParameterValue(1000);
    }

    // 更新视口
    UpdateInteractiveContext();

    // 通过 V3d_Viewer 创建视口
    m_hView = m_hViewer->CreateView();
    m_hView->ZBufferTriedronSetup(Quantity_NOC_RED, Quantity_NOC_GREEN, Quantity_NOC_BLUE, 0.8, 0.05, 12);
    m_hView->TriedronDisplay(Aspect_TOTP_LEFT_LOWER, Quantity_NOC_WHITE, 0.2, V3d_ZBUFFER);

    // 通过当前窗口句柄创建 WNT_Window，并将视口连接到 WNT_Window
    ui->frame->setAttribute(Qt::WA_PaintOnScreen);
    Handle(WNT_Window) H_WNT_Window = new WNT_Window((Aspect_Handle)ui->frame->winId());
    m_hView->SetWindow(H_WNT_Window);

    // 窗口映射
    if (!H_WNT_Window->IsMapped())
        H_WNT_Window->Map();

    // 设置视口
    m_hView->MustBeResized();
    m_hView->SetShadingModel(V3d_PHONG);

    // 设置白色背景
    Quantity_Color bgColor(1.0, 1.0, 1.0, Quantity_TOC_RGB);
    m_hView->SetBackgroundColor(bgColor);

    // 配置渲染参数，例如反走样、阴影等
    Graphic3d_RenderingParams &RenderParams = m_hView->ChangeRenderingParams();
    RenderParams.IsAntialiasingEnabled = true;
    RenderParams.NbMsaaSamples = 8;
    RenderParams.IsShadowEnabled = false;
    RenderParams.CollectedStats = Graphic3d_RenderingParams::PerfCounters_NONE;
}

void OCCWidget::UpdateInteractiveContext()
{
    // auto vertexShader = Graphic3d_ShaderObject::CreateFromFile(Graphic3d_TypeOfShaderObject::Graphic3d_TOS_VERTEX,
    //                                                            SHADER_PATH "/vertex.glsl");
    // auto fragmentShader =
    // Graphic3d_ShaderObject::CreateFromFile(Graphic3d_TypeOfShaderObject::Graphic3d_TOS_FRAGMENT,
    //                                                              SHADER_PATH "/fragment.glsl");
    // if (vertexShader.IsNull())
    // {
    //     qDebug() << "Failed to load vertex shader";
    // }
    // if (fragmentShader.IsNull())
    // {
    //     qDebug() << "Failed to load fragment shader";
    // }
    // Handle(Graphic3d_ShaderProgram) shaderProgram = new Graphic3d_ShaderProgram;
    // shaderProgram->AttachShader(vertexShader);
    // shaderProgram->AttachShader(fragmentShader);
    // if (shaderProgram->IsDone())
    // {
    //     qDebug() << "Shader program compiled successfully";
    // }

    // 在最前面获得辅助点，否则 RemoveAll 移除所有物体后，SelectedMesh 将无法获得正确的结果
    m_structure->Clear();
    if (MeshPtr mesh = SelectedMesh(); mesh && !m_boundaryNodes.empty())
    {
        Handle(Graphic3d_Group) aPrsGroup = m_structure->NewGroup();
        // Handle(Graphic3d_ArrayOfPoints) anArray = new Graphic3d_ArrayOfPoints(m_cornerNodes.size());
        for (const auto &boundaryNodes : m_boundaryNodes)
        {
            Handle(Graphic3d_ArrayOfSegments) anArray = new Graphic3d_ArrayOfSegments(boundaryNodes.size() * 2);
            for (int i = 1; i < boundaryNodes.size(); i++)
            {
                {
                    OpenMesh::VertexHandle vh(boundaryNodes[i - 1]);
                    OpenMesh::Vec3f pos = mesh->point(vh);
                    anArray->AddVertex(pos[0], pos[1], pos[2]);
                }

                {
                    OpenMesh::VertexHandle vh(boundaryNodes[i]);
                    OpenMesh::Vec3f pos = mesh->point(vh);
                    anArray->AddVertex(pos[0], pos[1], pos[2]);
                }
            }

            // Handle(Graphic3d_AspectMarker3d) anAspect =
            //     new Graphic3d_AspectMarker3d(Aspect_TOM_BALL, Quantity_NOC_BLUE, 2.0);
            Handle(Graphic3d_AspectLine3d) anAspect =
                new Graphic3d_AspectLine3d(Quantity_NOC_BLUE, Aspect_TypeOfLine::Aspect_TOL_SOLID, 2.0);
            aPrsGroup->AddPrimitiveArray(anArray);
            aPrsGroup->SetGroupPrimitivesAspect(anAspect);
        }
    }

    // 移除所有物体
    m_hAisContext->RemoveAll(false);

    // 遍历每个形状绘制，并激活选择风格
    for (auto sh : m_parent->m_shapes)
    {
        Handle(AIS_Shape) shape = new AIS_Shape(sh);
        shape->SetColor(Quantity_NOC_GREEN);
        // shape->Attributes()->ShadingAspect()->Aspect()->SetShaderProgram(shaderProgram);

        // 使用 false 会快很多
        m_hAisContext->Display(shape, false);
        m_hAisContext->SetDisplayMode(shape, AIS_Shaded, false);
    }

    // 绘制三角网格
    for (auto &&[tri, mesh] : m_parent->m_meshes)
        DrawTriangulation(tri);

    // 绘制点云
    for (auto cloud : m_parent->m_clouds)
    {
        cloud->SetColor(Quantity_NOC_BLUE);
        cloud->Attributes()->ShadingAspect()->Aspect()->SetMarkerScale(5);
        m_hAisContext->Display(cloud, false);
    }

    // 绘制辅助点云
    m_structure->Display();

    // 绘制坐标轴
    m_hAisContext->Display(m_trihedron, false);

    // 修改选择风格
    AdjustSelectionStyle(m_hAisContext);

    // 激活选择
    m_hAisContext->Activate(8, true); // triangle
    m_hAisContext->Activate(4, true); // faces
    m_hAisContext->Activate(2, true); // edges
    // m_hAisContext->Activate(1, true);  // vertex

    // 缩放以显示全部物体
    // FitAllShapes();
}

void OCCWidget::AdjustSelectionStyle(const Handle(AIS_InteractiveContext) & context)
{
    // 设置渲染信息以及选中物体的高亮信息
    Handle(Prs3d_Drawer) selDrawer = new Prs3d_Drawer;

    selDrawer->SetLink(context->DefaultDrawer());
    selDrawer->SetFaceBoundaryDraw(true);
    selDrawer->SetDisplayMode(1);
    selDrawer->SetTransparency(1.0f);
    selDrawer->SetZLayer(Graphic3d_ZLayerId_Topmost);
    selDrawer->SetColor(Quantity_NOC_BLUE);
    selDrawer->SetBasicFillAreaAspect(new Graphic3d_AspectFillArea3d());

    // 调整填充区域颜色材质等信息
    const Handle(Graphic3d_AspectFillArea3d) &fillArea = selDrawer->BasicFillAreaAspect();

    fillArea->SetInteriorColor(Quantity_NOC_GREEN);
    fillArea->SetBackInteriorColor(Quantity_NOC_GREEN);

    fillArea->ChangeFrontMaterial().SetMaterialName(Graphic3d_NOM_NEON_GNC);
    fillArea->ChangeFrontMaterial().SetTransparency(1.0f);
    fillArea->ChangeBackMaterial().SetMaterialName(Graphic3d_NOM_NEON_GNC);
    fillArea->ChangeBackMaterial().SetTransparency(1.0f);

    selDrawer->UnFreeBoundaryAspect()->SetWidth(1.0);

    // 更新 AIS context
    context->SetHighlightStyle(Prs3d_TypeOfHighlight_LocalSelected, selDrawer);
}

void OCCWidget::FitAllShapes()
{
    // 存在物体则适应物体
    auto window = static_cast<MainWindow *>(parentWidget());
    if (!m_parent->m_shapes.empty() || !m_parent->m_clouds.empty() || !m_parent->m_meshes.empty())
        m_hView->FitAll();
}

void OCCWidget::SetShadeMode(ShadeMode mode)
{
    m_shadeMode = mode;
}

void OCCWidget::DrawTriangulation(Handle(Poly_Triangulation) triangle)
{
    // 一般三角化
    if (m_shadeMode == ShadeMode::Smooth)
    {
        // Handle(AIS_InteractiveObject) triObject = new AIS_Triangulation(triangle);
        Handle(AIS_Mesh) triObject = new AIS_Mesh(triangle);
        triObject->SetColor(Quantity_NOC_GREEN);
        m_hAisContext->SetDisplayMode(triObject, AIS_WireFrame, true);
        m_hAisContext->Display(triObject, Standard_True);
        // triObject->Attributes()->ShadingAspect()->Aspect()->SetInteriorStyle(Aspect_IS_SOLID);
    }
    else
    {
        // 载入网格数据
        Handle(OCCMesh) mesh = new OCCMesh(Standard_True);
        Handle(OCCMesh_Data) aDS = new OCCMesh_Data(triangle);
        mesh->AddMeshBuilder();
        mesh->AddDataSource(aDS);
        m_hAisContext->Display(mesh, Standard_True);
        m_hAisContext->Activate(mesh, Standard_True);
        // 这一项必须启用才能选择顶点
    }
}

void OCCWidget::UpdateSelectedNodes()
{
    std::vector<int> cornerNodes;
    for (m_hAisContext->InitSelected(); m_hAisContext->MoreSelected(); m_hAisContext->NextSelected())
    {
        // 检查是否是 MeshVS_Mesh
        Handle(MeshVS_Mesh) selectedMesh = Handle(MeshVS_Mesh)::DownCast(m_hAisContext->SelectedInteractive());
        if (!selectedMesh.IsNull())
        {
            Handle(MeshVS_MeshEntityOwner) owner =
                Handle(MeshVS_MeshEntityOwner)::DownCast(m_hAisContext->SelectedOwner());
            if (!owner.IsNull())
            {
                std::cout << "Selected node index: " << owner->ID() << std::endl;
                Handle(OCCMesh_Data) data = Handle(OCCMesh_Data)::DownCast(selectedMesh->GetDataSource());

                TColStd_Array1OfReal Coords(1, 3);
                Standard_Integer NbNodes;
                MeshVS_EntityType Type;
                if (data->GetGeom(owner->ID(), false, Coords, NbNodes, Type))
                {
                    cornerNodes.push_back(owner->ID() - 1);

                    // Mesh &mesh = *m_parent->m_meshes[data->GetTriangulation()];
                    // OpenMesh::VertexHandle vh(owner->ID()); // 创建顶点句柄
                    // OpenMesh::Vec3f pos = mesh.point(vh);       // 获取顶点坐标
                    // gp_Pnt node(Coords(1), Coords(2), Coords(3));
                    // std::cout << "Selected node position: " << node.X() << " " << node.Y() << " " << node.Z()
                    //           << std::endl;
                    // std::cout << "Selected vertex position: " << pos[0] << " " << pos[1] << " " << pos[2] <<
                    // std::endl;
                }
            }
        }
    }

    if (m_cornerNodes.empty())
        m_cornerNodes.push_back(std::vector<int>{});

    if (!cornerNodes.empty())
    {
        // 每个边界只需要 4 个顶点
        if (m_cornerNodes.back().size() == 4)
        {
            m_cornerNodes.push_back(std::vector<int>{});
            m_boundaryNodes.push_back(std::vector<int>{});
        }

        for (int i = 0; i < cornerNodes.size(); i++)
            m_cornerNodes.back().push_back(cornerNodes[i]);
    }
    else
        m_cornerNodes.back().clear();

    UpdateBoundaryNodes();

    // 不要更新，避免 Selected 被清空
    // UpdateInteractiveContext();
}

void OCCWidget::UpdateBoundaryNodes()
{
    if (m_boundaryNodes.empty())
        m_boundaryNodes.push_back(std::vector<int>{});

    m_boundaryNodes.back().clear();
    if (m_cornerNodes.back().empty())
        return;

    int startIdx = m_cornerNodes.back()[0];
    OpenMesh::VertexHandle start_vh(startIdx);
    MeshPtr mesh = SelectedMesh();

    if (!mesh->is_boundary(start_vh))
    {
        std::cerr << "Start vertex is not on boundary!" << std::endl;
        m_cornerNodes.back().clear();
        return;
    }

    OpenMesh::VertexHandle current_vh = start_vh;
    OpenMesh::HalfedgeHandle current_heh;

    // Find the first outgoing boundary halfedge from start vertex
    for (auto heh : mesh->voh_range(current_vh))
    {
        if (mesh->is_boundary(heh))
        {
            current_heh = heh;
            break;
        }
    }

    if (!current_heh.is_valid())
    {
        std::cerr << "No boundary halfedge found from start vertex!" << std::endl;
        return;
    }

    // Traverse the boundary loop
    do
    {
        m_boundaryNodes.back().push_back(current_vh.idx());

        current_heh = mesh->next_halfedge_handle(current_heh);
        current_vh = mesh->to_vertex_handle(current_heh);

    } while (current_vh != start_vh && m_boundaryNodes.size() < mesh->n_vertices());

    // Optional: close the loop
    if (current_vh == start_vh)
        m_boundaryNodes.back().push_back(start_vh.idx());
}

std::vector<std::vector<int>> OCCWidget::CornerNodes()
{
    return m_cornerNodes;
}

std::vector<std::vector<int>> OCCWidget::BoundaryNodes()
{
    return m_boundaryNodes;
}

void OCCWidget::ClearNodes()
{
    m_cornerNodes.clear();
    m_boundaryNodes.clear();
}

std::vector<TopoDS_Shape> OCCWidget::SelectedShapes()
{
    std::vector<TopoDS_Shape> shapeList;
    for (m_hAisContext->InitSelected(); m_hAisContext->MoreSelected(); m_hAisContext->NextSelected())
    {
        Handle(SelectMgr_EntityOwner) anOwner = m_hAisContext->SelectedOwner();
        Handle(AIS_InteractiveObject) anObj = Handle(AIS_InteractiveObject)::DownCast(anOwner->Selectable());

        // 将 SelectMgr_EntityOwner 对象下降为 StdSelect_BRepOwner 对象
        if (Handle(StdSelect_BRepOwner) aBRepOwner = Handle(StdSelect_BRepOwner)::DownCast(anOwner))
        {
            TopoDS_Shape aShape = aBRepOwner->Shape();
            shapeList.push_back(aShape);
        }
    }
    return shapeList;
}

Handle(AIS_PointCloud) OCCWidget::SelectedCloud()
{
    for (m_hAisContext->InitSelected(); m_hAisContext->MoreSelected(); m_hAisContext->NextSelected())
    {
        Handle(SelectMgr_EntityOwner) anOwner = m_hAisContext->SelectedOwner();
        Handle(AIS_PointCloud) pointCloud = Handle(AIS_PointCloud)::DownCast(anOwner->Selectable());
        if (!pointCloud.IsNull())
            return pointCloud;
    }
    return Handle(AIS_PointCloud){};
}

MeshPtr OCCWidget::SelectedMesh()
{
    for (m_hAisContext->InitSelected(); m_hAisContext->MoreSelected(); m_hAisContext->NextSelected())
    {
        // 检查是否是 OCCMesh
        Handle(OCCMesh) selectedMesh = Handle(OCCMesh)::DownCast(m_hAisContext->SelectedInteractive());
        if (!selectedMesh.IsNull())
        {
            Handle(OCCMesh_Data) data = Handle(OCCMesh_Data)::DownCast(selectedMesh->GetDataSource());
            return m_parent->m_meshes[data->GetTriangulation()];
        }
    }
    return nullptr;
}

void OCCWidget::resizeEvent(QResizeEvent *)
{
    ui->frame->resize(size());
    // qDebug() << size();

    // 重绘视口
    if (m_hView.IsNull() == FALSE)
        m_hView->MustBeResized();
}

void OCCWidget::paintEvent(QPaintEvent *event)
{
    // 重绘视口
    if (m_hView.IsNull() == FALSE)
    {
        m_hView->MustBeResized();
        m_hView->Redraw();
    }
}

void OCCWidget::mousePressEvent(QMouseEvent *event)
{
    QPoint pos = event->pos();

    if (event->button() == Qt::LeftButton)
    {
        Handle(AIS_InteractiveContext) ctx = m_hAisContext;
        ctx->Select(Standard_True);

        UpdateSelectedNodes();
    }
    else if (event->button() == Qt::MiddleButton)
    {
        // 如果未选中，则进入旋转模式
        m_mode = Action3d::Rotation;
        m_hView->StartRotation(pos.x(), pos.y());
    }
    else if (event->button() == Qt::RightButton)
    {
        // 若选中，则进入平移模式
        m_mode = Action3d::Panning;
        m_pos = pos;
    }
}

void OCCWidget::mouseReleaseEvent(QMouseEvent *event)
{
    // 松开鼠标退出模式
    m_mode = Action3d::Nothing;
}

void OCCWidget::mouseMoveEvent(QMouseEvent *event)
{
    QPoint pos = event->pos();

    switch (m_mode)
    {
    case Action3d::Nothing: {
        // 判断是否选中
        Handle(AIS_InteractiveContext) ctx = m_hAisContext;
        ctx->MoveTo(pos.x(), pos.y(), m_hView, Standard_True);
        break;
    }
    case Action3d::Panning: {
        // 平移视角
        m_hView->Pan(pos.x() - m_pos.x(), -pos.y() + m_pos.y());
        m_pos = pos;
        break;
    }
    case Action3d::Rotation: {
        // 旋转视角
        m_hView->Rotation(pos.x(), pos.y());
        break;
    }
    }

    // 鼠标移动就刷新
    m_hView->Redraw();
}

void OCCWidget::wheelEvent(QWheelEvent *event)
{
    QPoint delta = event->angleDelta();

    if (m_mode == Action3d::Nothing)
    {
        // 缩放视口
        if (delta.y() > 0)
        {
            double scar = 1.2;
            if (m_hView->Scale() <= 10000)
                m_hView->SetZoom(scar);
        }
        else
        {
            double scar = 0.8;
            if (m_hView->Scale() > 0.0001)
                m_hView->SetZoom(scar);
        }
    }
}

QPaintEngine *OCCWidget::paintEngine() const
{
    return nullptr;
}
